#include "lora_protocol_v1.h"
#include <sr2valhelper.h>
#include <chrono>
#include <sstream>
#include <iomanip>
#include <iostream>

namespace cores
{
    namespace protocols
    {
        namespace lora_v1
        {
            using namespace cores::datas;

            LoraProtocol::LoraProtocol()
            {
            }

            LoraProtocol::~LoraProtocol()
            {
            }

            int LoraProtocol::length() const
            {
                return 4 + this->len();
            }

            int LoraProtocol::toBuffer(uint8_t *buffer, const int &size)
            {
                uint16_t s = this->len();
                if (s > size)
                    return 0;

                auto pos = 0;
                buffer[pos++] = _header;
                buffer[pos++] = (s & 0xff); // 低字节
                buffer[pos++] = (s >> 8);   // 高字节
                auto l = writeToBuffer(buffer + pos, size - pos);
                auto crc = crc16(buffer + pos, l);
                pos += l;
                buffer[pos++] = crc >> 8;
                buffer[pos++] = (crc & 0xff);
                buffer[pos++] = _tailer;
                if (pos != length())
                    return 0;

                return pos;
            }

            void LoraProtocol::setHeader(const uint8_t &header)
            {
                this->_header = header;
            }

            void LoraProtocol::setTailer(const uint8_t &tailer)
            {
                this->_tailer = tailer;
            }

            uint8_t LoraProtocol::header() const
            {
                return this->_header;
            }

            uint8_t LoraProtocol::tailer() const
            {
                return this->_tailer;
            }

            uint16_t LoraProtocol::crc16(const uint8_t *buffer, const int &size)
            {
                const uint16_t polynomial = 0xa001; //  使用多项式
                uint16_t crc = 0x0000;              //  初始CRC值
                uint16_t flag;

                for (auto current_byte = 0; current_byte < size; current_byte++)
                {
                    crc ^= (buffer[current_byte] << 8);

                    for (uint8_t i = 0; i < 8; i++)
                    {
                        flag = crc & 0x8000;
                        crc <<= 1;
                        if (flag)
                            crc ^= polynomial;
                    }
                }

                return crc;
            }

            TransTokenLoraProtocol::TransTokenLoraProtocol(
                const uint16_t &loraId,
                const uint8_t &nozzle,
                const uint32_t &now_time,
                const uint16_t &serialnum)
                : LoraProtocol(), _loraId(loraId), _nozzleCode(nozzle), _timestamp(now_time), _serialNumber(serialnum)
            {
            }

            TransTokenLoraProtocol::~TransTokenLoraProtocol()
            {
            }

            int TransTokenLoraProtocol::len() const
            {
                return 12;
            }

            int TransTokenLoraProtocol::writeToBuffer(uint8_t *buffer, const int &size)
            {
                auto pos = 0;
                buffer[pos++] = 0x01;
                buffer[pos++] = _loraId & 0xff;
                buffer[pos++] = _loraId >> 8;
                buffer[pos++] = _nozzleCode;
                buffer[pos++] = _timestamp & 0xff;
                buffer[pos++] = (_timestamp >> 8) & 0xff;
                buffer[pos++] = (_timestamp >> 16) & 0xff;
                buffer[pos++] = (_timestamp >> 24) & 0xff;
                buffer[pos++] = _serialNumber & 0xff;
                buffer[pos++] = _serialNumber >> 8;

                return pos;
            }

            TaxTokenLoraProtocol::TaxTokenLoraProtocol(
                const uint16_t &loraId,
                const uint8_t &taxId,
                const uint32_t &now_time,
                const uint16_t &serialnum)
                : LoraProtocol(), _loraId(loraId), _taxId(taxId), _timestamp(now_time), _serialNumber(serialnum)
            {
            }

            TaxTokenLoraProtocol::~TaxTokenLoraProtocol()
            {
            }

            int TaxTokenLoraProtocol::len() const
            {
                return 12;
            }

            int TaxTokenLoraProtocol::writeToBuffer(uint8_t *buffer, const int &size)
            {
                auto pos = 0;
                buffer[pos++] = 0x11;
                buffer[pos++] = _loraId & 0xff;
                buffer[pos++] = _loraId >> 8;
                buffer[pos++] = _taxId;
                buffer[pos++] = _timestamp & 0xff;
                buffer[pos++] = (_timestamp >> 8) & 0xff;
                buffer[pos++] = (_timestamp >> 16) & 0xff;
                buffer[pos++] = (_timestamp >> 24) & 0xff;
                buffer[pos++] = _serialNumber & 0xff;
                buffer[pos++] = _serialNumber >> 8;

                return pos;
            }

            ReadLoraParamProtocol::ReadLoraParamProtocol(
                const uint16_t &loraId,
                const uint32_t &now_time)
                : LoraProtocol(), _loraId(loraId), _timestamp(now_time)
            {
            }

            ReadLoraParamProtocol::~ReadLoraParamProtocol()
            {
            }

            int ReadLoraParamProtocol::len() const
            {
                return 9;
            }

            int ReadLoraParamProtocol::writeToBuffer(uint8_t *buffer, const int &size)
            {
                auto pos = 0;
                buffer[pos++] = 0xA1;
                buffer[pos++] = _loraId & 0xff;
                buffer[pos++] = _loraId >> 8;
                buffer[pos++] = _timestamp & 0xff;
                buffer[pos++] = (_timestamp >> 8) & 0xff;
                buffer[pos++] = (_timestamp >> 16) & 0xff;
                buffer[pos++] = (_timestamp >> 24) & 0xff;
                return pos;
            }

            DownLoraParamProtocol::DownLoraParamProtocol(
                const uint16_t &loraId,
                std::vector<LoraParam> &params,
                const uint32_t &now_time)
                : LoraProtocol(), _loraId(loraId), _timestamp(now_time)
            {
                _params.swap(params);
            }

            DownLoraParamProtocol::~DownLoraParamProtocol()
            {
            }

            int DownLoraParamProtocol::len() const
            {
                return 10 + _params.size() * 3;
            }

            int DownLoraParamProtocol::writeToBuffer(uint8_t *buffer, const int &size)
            {
                auto pos = 0;
                buffer[pos++] = 0xA9;
                buffer[pos++] = _loraId & 0xff;
                buffer[pos++] = _loraId >> 8;
                buffer[pos++] = _params.size();
                for (auto &param : _params)
                {
                    buffer[pos++] = param.nozzle_code;
                    buffer[pos++] = param.category;
                    buffer[pos++] = param.screen_type;
                }
                buffer[pos++] = _timestamp & 0xff;
                buffer[pos++] = (_timestamp >> 8) & 0xff;
                buffer[pos++] = (_timestamp >> 16) & 0xff;
                buffer[pos++] = (_timestamp >> 24) & 0xff;
                return pos;
            }

            LoraUploadProtocol::LoraUploadProtocol(const uint8_t *buffer, const int &size)
            {
                _buffer.reserve(size);
                for (auto i = 0; i < size; i++)
                    _buffer.push_back(buffer[i]);
            }

            LoraUploadProtocol::~LoraUploadProtocol()
            {
            }

            bool LoraUploadProtocol::isValid() const
            {
                return _isValid;
            }

            inline uint32_t LoraUploadProtocol::now()
            {
                return std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
            }

            LoraUploadProtocolParser::LoraUploadProtocolParser()
            {
            }

            LoraUploadProtocolParser::~LoraUploadProtocolParser()
            {
            }

            bool LoraUploadProtocolParser::isChecked(const uint8_t *buffer, const int &size)
            {
                if (size <= 4 || buffer[0] != _header)
                {
                    // std::cout << __FILE__ << ":" << __LINE__ << std::endl;
                    return false;
                }

                // 计算长度
                uint16_t s = buffer[1] + (buffer[2] << 8);
                if (s + 4 > size || buffer[s + 3] != _tailer) // 一字节协议头，一字节协议尾，一字节协议内容长度（协议内容长度包括2字节校验码）
                    return false;

                // std::cout << __FILE__ << ":" << __LINE__ << std::endl;
                auto crc = crc16(buffer + 3, s - 2);
                // uint16_t r = *((uint16_t*)(buffer + s + 1));//
                uint16_t r = (buffer[s + 1] << 8) + buffer[s + 2];

                // std::cout << "crc = " << std::hex << std::setw(4) << std::setfill('0') << crc << std::endl;
                // std::cout << "r = " << std::hex << std::setw(4) << std::setfill('0') << r << std::endl;
                if (crc != r)
                    return false;

                // std::cout << __FILE__ << ":" << __LINE__ << std::endl;
                return true;
            }

            std::shared_ptr<LoraUploadProtocol> LoraUploadProtocolParser::parse(
                const uint8_t *buffer,
                const int &size)
            {
                return std::shared_ptr<LoraUploadProtocol>();
            }

            uint16_t LoraUploadProtocolParser::crc16(const uint8_t *buffer, const int &size)
            {
                const uint16_t polynomial = 0xa001; //  使用多项式
                uint16_t crc = 0x0000;              //  初始CRC值
                uint16_t flag;

                for (auto current_byte = 0; current_byte < size; current_byte++)
                {
                    crc ^= (buffer[current_byte] << 8);

                    for (uint8_t i = 0; i < 8; i++)
                    {
                        flag = crc & 0x8000;
                        crc <<= 1;
                        if (flag)
                            crc ^= polynomial;
                    }
                }

                return crc;
            }

            TransDataUpload::TransDataUpload(const uint8_t *buffer, const int &size)
                : LoraUploadProtocol(buffer, size)
            {
                if (buffer[3] != 0x81)
                    return;

                _transData.data_sn = buffer[4] + (buffer[5] << 8);
                _transData.lora_id = buffer[6] + (buffer[7] << 8);
                _transData.nozzle_code = buffer[8];
                _transData.beg_time = *((uint32_t *)(buffer + 9));
                _transData.end_time = *((uint32_t *)(buffer + 13));
                _transData.category = buffer[17];
                _transData.encoder_val = *((uint32_t *)(buffer + 18));
                _transData.screen_type = buffer[22];
                auto temp = SR2ValueManager::getInstance()->getSR2ValHelper(_transData.screen_type)->HexChar2ScreenVal(buffer + 23, 12);
                _transData.amount = temp.amount; // 金额
                _transData.price = temp.price;   // 单价
                _transData.volume = temp.volume; // 油量

                _transData.trans_beg_time = _transData.beg_time;
                _transData.trans_end_time = _transData.end_time;
                _transData.create_time = now();
                _transData.sort = 0;
                _transData.type = buffer[47] | buffer[48];
                _transData.screen_raw_type = 0;

                // 将原始的屏数据转换成字符串
                _transData.screen_raw_data = toString(buffer + 23, 24);
                _isValid = true;
            }

            TransDataUpload::~TransDataUpload()
            {
            }

            OilTransaction TransDataUpload::transData() const
            {
                return _transData;
            }

            uint16_t TransDataUpload::loraId() const
            {
                return _transData.lora_id;
            }

            std::string TransDataUpload::toString(const uint8_t *buffer, const int &size)
            {
                std::stringstream ss;
                ss << std::hex;
                for (auto i = 0; i < size; i++)
                {
                    if (i == 0)
                        ss << setfill('0') << setw(2) << int(buffer[i]);
                    else
                        ss << " " << setfill('0') << setw(2) << int(buffer[i]);
                }
                return ss.str();
            }

            HasNoTransDataUpload::HasNoTransDataUpload(const uint8_t *buffer, const int &size)
                : LoraUploadProtocol(buffer, size)
            {
                if (buffer[3] != 0x80)
                    return;

                _serialNumber = buffer[4] + (buffer[5] << 8); // 4~5
                _loraId = buffer[6] + (buffer[7] << 8);       // 6~7
                _nozzelCode = buffer[8];                      // 8
                _currentTime = *((uint32_t *)(buffer + 9));   // 9~12
                _errorCode = *((uint16_t *)(buffer + 13));    // 13~14

                _isValid = true;
            }

            HasNoTransDataUpload::~HasNoTransDataUpload()
            {
            }

            uint16_t HasNoTransDataUpload::serialNumber() const
            {
                return this->_serialNumber;
            }

            uint16_t HasNoTransDataUpload::loraId() const
            {
                return this->_loraId;
            }

            uint8_t HasNoTransDataUpload::nozzle() const
            {
                return this->_nozzelCode;
            }

            uint32_t HasNoTransDataUpload::time() const
            {
                return this->_currentTime;
            }

            uint16_t HasNoTransDataUpload::errorCode() const
            {
                return this->_errorCode;
            }

            HasLoraParamUpload::HasLoraParamUpload(const uint8_t *buffer, const int &size)
                : LoraUploadProtocol(buffer, size)
            {
                if (buffer[3] != 0xb1)
                    return;

                _loraId = buffer[4] + (buffer[5] << 8);
                _params.reserve(buffer[6]);
                auto pos = 7;
                for (auto i = 0; i < buffer[6]; i++)
                {
                    _params.push_back({buffer[pos++], buffer[pos++], buffer[pos++]});
                }

                _timestamp = *((uint32_t *)(buffer + pos));
                pos += 4;
                _errorCode = *((uint16_t *)(buffer + pos));
                _isValid = true;
            }

            HasLoraParamUpload::~HasLoraParamUpload()
            {
            }

            uint16_t HasLoraParamUpload::loraId() const
            {
                return _loraId;
            }

            uint32_t HasLoraParamUpload::time() const
            {
                return _timestamp;
            }

            uint16_t HasLoraParamUpload::errorCode() const
            {
                return _errorCode;
            }

            std::vector<NozzleParam> HasLoraParamUpload::getNozzleParams() const
            {
                return _params;
            }

            HasNoLoraParamUpload::HasNoLoraParamUpload(const uint8_t *buffer, const int &size)
                : LoraUploadProtocol(buffer, size)
            {
                if (buffer[3] != 0xb0)
                    return;

                _loraId = buffer[4] + (buffer[5] << 8);
                _timestamp = *((uint32_t *)(buffer + 7));
                _errorCode = *((uint16_t *)(buffer + 11));
                _isValid = true;
            }

            HasNoLoraParamUpload::~HasNoLoraParamUpload()
            {
            }

            uint16_t HasNoLoraParamUpload::loraId() const
            {
                return this->_loraId;
            }

            uint32_t HasNoLoraParamUpload::time() const
            {
                return _timestamp;
            }

            uint16_t HasNoLoraParamUpload::errorCode() const
            {
                return _errorCode;
            }

            DownloadLoraParamAck::DownloadLoraParamAck(const uint8_t *buffer, const int &size)
                : LoraUploadProtocol(buffer, size)
            {
                if (buffer[3] != 0xb9)
                    return;

                _loraId = buffer[4] + (buffer[5] << 8);
                _timestamp = *((uint32_t *)(buffer + 6));
                _errorCode = *((uint16_t *)(buffer + 10));
                _isValid = true;
            }

            DownloadLoraParamAck::~DownloadLoraParamAck()
            {
            }

            uint16_t DownloadLoraParamAck::loraId() const
            {
                return _loraId;
            }

            uint32_t DownloadLoraParamAck::time() const
            {
                return _timestamp;
            }

            uint16_t DownloadLoraParamAck::errorCode() const
            {
                return _errorCode;
            }
        }
    }
}